package cn.funeralobjects.upms.service.impl;

import cn.funeralobjects.common.jpa.service.CommonCloudJpaService;
import cn.funeralobjects.common.jpa.service.CommonIntTreeCrudJpaService;
import cn.funeralobjects.common.repository.CommonCloudRepository;
import cn.funeralobjects.common.service.annotataion.CommonRollback;
import cn.funeralobjects.common.service.annotataion.CommonService;
import cn.funeralobjects.common.service.exception.DataConflictException;
import cn.funeralobjects.common.service.validate.DataCheck;
import cn.funeralobjects.upms.entity.*;
import cn.funeralobjects.upms.model.Group;
import cn.funeralobjects.upms.repository.GroupAffiliationRepository;
import cn.funeralobjects.upms.repository.GroupPathRepository;
import cn.funeralobjects.upms.repository.GroupRepository;
import cn.funeralobjects.upms.repository.GroupTagRepository;
import cn.funeralobjects.upms.service.GroupService;
import cn.funeralobjects.util.Assert;
import cn.funeralobjects.util.annotation.ArgHasLength;
import cn.funeralobjects.util.annotation.ArgNotZero;
import cn.funeralobjects.util.annotation.AssertArg;
import com.querydsl.core.types.EntityPath;
import com.querydsl.core.types.Predicate;
import com.querydsl.core.types.dsl.NumberPath;
import com.querydsl.core.types.dsl.SimpleExpression;
import com.querydsl.jpa.JPQLQueryFactory;
import org.springframework.data.repository.CrudRepository;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

/**
 * GroupService的实现类
 *
 * @author FuneralObjects
 * Create date: 2020/4/23 5:15 PM
 */
@CommonService
public class GroupServiceImpl implements GroupService, CommonCloudJpaService<EGroup, Integer>, CommonIntTreeCrudJpaService<EGroupPath, EGroupAffiliation, EGroupAffiliationPk, Group, EGroup> {
    @Resource
    private GroupRepository groupRepository;

    @Resource
    private GroupPathRepository groupPathRepository;

    @Resource
    private GroupAffiliationRepository groupAffiliationRepository;

    @Resource
    private JPQLQueryFactory jpqlQueryFactory;

    @Resource
    private GroupTagRepository groupTagRepository;

    @CommonRollback
    @AssertArg
    @Override
    public void addGroupTag(Integer groupId, @ArgHasLength String tag) {
        mustExistsById(groupId);
        groupTagRepository.save(new EGroupTag().setGroupId(groupId).setTag(tag));
    }

    @CommonRollback
    @AssertArg
    @Override
    public void removeGroupTag(@ArgNotZero Integer groupId, @ArgHasLength String tag) {
        groupTagRepository.delete(new EGroupTag().setGroupId(groupId).setTag(tag));
    }

    @AssertArg
    @Override
    public List<String> getTags(@ArgNotZero Integer groupId) {
        return jpqlQueryFactory.select(QEGroupTag.eGroupTag.tag)
                .from(QEGroupTag.eGroupTag)
                .innerJoin(QEGroup.eGroup).on(QEGroup.eGroup.id.eq(QEGroupTag.eGroupTag.groupId))
                .where(QEGroup.eGroup.delSalt.eq(getExistsDelSalt()).and(QEGroupTag.eGroupTag.groupId.eq(groupId)))
                .fetch();
    }

    @AssertArg
    @Override
    public List<Integer> findGroupIdByTag(@ArgHasLength String tag) {
        return jpqlQueryFactory.select(QEGroupTag.eGroupTag.groupId)
                .from(QEGroupTag.eGroupTag)
                .innerJoin(QEGroup.eGroup).on(QEGroup.eGroup.id.eq(QEGroupTag.eGroupTag.groupId))
                .where(QEGroup.eGroup.delSalt.eq(getExistsDelSalt()).and(QEGroupTag.eGroupTag.tag.eq(tag)))
                .fetch();
    }


    @Override
    public void notConflict(String name) {
        DataCheck.conflictData(existsByName(name), () -> createDataCode("name", name));
    }

    @Override
    public void notConflict(String name, Integer withoutId) {
        DataCheck.conflictData(existsByName(name, withoutId), () -> createDataCode("name", name));
    }

    @AssertArg
    @Override
    public List<Integer> getPathById(@ArgNotZero Integer id) {
        return groupPathRepository.findById(id).map(EGroupPath::getPath).map(pathConverter()::convertToEntityAttribute).orElseGet(() -> new ArrayList<>(0));
    }

    private Integer getExistsDelSalt() {
        return (Integer) groupRepository.getDefaultExistsDelSalt();
    }

    @Override
    public JPQLQueryFactory jpqlQueryFactory() {
        return jpqlQueryFactory;
    }

    @Override
    public SimpleExpression<Integer> getIdDsl() {
        return QEGroup.eGroup.id;
    }

    @Override
    public EntityPath<EGroup> getEntityPath() {
        return QEGroup.eGroup;
    }

    @Override
    public Predicate getExistsPredicate() {
        return QEGroup.eGroup.delSalt.eq(getExistsDelSalt());
    }

    @Override
    public CommonCloudRepository<EGroup, Integer> commonCloudRepository() {
        return groupRepository;
    }

    @Override
    public void checkConflict(Group group) throws DataConflictException {
        validateModify(group);
        DataCheck.conflictData(existsByName(group.getName()), () -> createDataCode("name", group.getName()));
    }

    @Override
    public EGroup toEntity(Group group) {
        return Optional.ofNullable(group)
                .map(g -> {
                    EGroup entity = new EGroup();
                    entity.setCode(g.getCode())
                            .setName(g.getName());
                    return entity;
                }).orElse(null);
    }

    @Override
    public CrudRepository<EGroup, Integer> modifyRepository() {
        return groupRepository;
    }
    @Override
    public CrudRepository<EGroupAffiliation, EGroupAffiliationPk> affiliationJpaRepository() {
        return groupAffiliationRepository;
    }

    @Override
    public CrudRepository<EGroupPath, Integer> pathJpaRepository() {
        return groupPathRepository;
    }

    @Override
    public EGroupAffiliationPk createAffiliationPk(Integer id, Integer paternalId) {
        EGroupAffiliationPk pk = new EGroupAffiliationPk();
        pk.setId(id).setPaternalId(paternalId);
        return pk;
    }

    @Override
    public EGroupAffiliation createAffiliation(EGroupAffiliationPk pk, Short gen) {
        EGroupAffiliation affiliation = new EGroupAffiliation();
        affiliation.setGen(gen).setPk(pk);
        return affiliation;
    }

    @Override
    public EGroupPath createEntityPath(Integer id, String path) {
        return Optional.of(new EGroupPath())
                .map(entity -> {
                    entity.setId(id).setPath(path);
                    return entity;
                }).get();
    }

    @Override
    public void checkConflict(Group group, Integer withoutId) throws DataConflictException {
        validateModify(group);
        DataCheck.conflictData(existsByName(group.getName(), withoutId), () -> createDataCode("name", group.getName()));
    }

    @Override
    public void validateModify(Group group) {
        Assert.argAssert(group, "group");
    }

    @Override
    public void copyToEntity(Group sourceModify, EGroup targetEntity) {
        targetEntity.setCode(sourceModify.getCode())
                .setName(sourceModify.getName());
    }

    @Override
    public EntityPath<EGroupAffiliation> getAffiliationEntityPath() {
        return QEGroupAffiliation.eGroupAffiliation;
    }

    @Override
    public SimpleExpression<Integer> getAffiliationPkIdDsl() {
        return QEGroupAffiliation.eGroupAffiliation.pk.id;
    }

    @Override
    public SimpleExpression<Integer> getAffiliationPkPaternalIdDsl() {
        return QEGroupAffiliation.eGroupAffiliation.pk.paternalId;
    }

    @Override
    public SimpleExpression<Integer> getParentIdDsl() {
        return QEGroup.eGroup.parentId;
    }

    @Override
    public NumberPath<Short> getAffiliationGenDsl() {
        return QEGroupAffiliation.eGroupAffiliation.gen;
    }
}
